#pragma warning(disable : 4355)
#include "Avalon/Network/TcpSocketReader.hpp"
#include <boost/bind.hpp>
#include "Avalon/IO/Buffer.hpp"
#include "Avalon/Network/SocketException.hpp"
#include "Avalon/Network/SocketType.hpp"
#include "Avalon/Network/TcpSocketConnection.hpp"

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::Network;
using namespace Avalon::Network::Detail;
using namespace Avalon::Threading;
using namespace boost;
using namespace boost::asio;
using namespace boost::asio::ip;
using namespace std;

namespace {
  const int DEFAULT_READ_SIZE = 1024;
}

TcpSocketReader::~TcpSocketReader() {}

void TcpSocketReader::Read(Buffer* destination, Out<Async<int> > sizeRead) {
  Read(destination, m_readSize, Store(sizeRead));
}

void TcpSocketReader::Read(void* destination, int size,
    Out<Async<int> > sizeRead) {
  m_sizeRead = sizeRead.Get();
  m_socket->m_socket.async_read_some(buffer(destination, size), m_dataReadSlot);
}

void TcpSocketReader::Read(Buffer* destination, int size,
    Out<Async<int> > sizeRead) {
  m_readSize = size;
  m_sizeRead = sizeRead.Get();
  int readOffset = destination->GetSize();
  destination->Grow(m_readSize);
  m_buffer = destination;
  m_socket->m_socket.async_read_some(buffer(destination->GetMutableData() +
    readOffset, m_readSize), m_bufferReadSlot);
}

TcpSocketReader::TcpSocketReader(const shared_ptr<SocketType>& socket)
    : m_socket(socket),
      m_readSize(DEFAULT_READ_SIZE),
      m_dataReadSlot(bind(&TcpSocketReader::OnDataRead, this, _1, _2)),
      m_bufferReadSlot(bind(&TcpSocketReader::OnBufferRead, this, _1, _2)) {}

void TcpSocketReader::OnDataRead(const system::error_code& error,
    size_t size) {
  if(error != 0) {
    m_sizeRead->SetException(SocketException(error.value(), error.message()));
    return;
  }
  assert(size != 0);
  m_sizeRead->SetResult(static_cast<int>(size));
}

void TcpSocketReader::OnBufferRead(const system::error_code& error,
    size_t size) {
  if(error != 0) {
    m_buffer->Shrink(m_readSize);
    m_sizeRead->SetException(SocketException(error.value(), error.message()));
    return;
  }
  assert(size != 0);
  m_buffer->Shrink(m_readSize - size);
  if(size == m_readSize) {
    m_readSize *= 2;
  }
  m_sizeRead->SetResult(static_cast<int>(size));
}
